Im dritten und letzten Kursteil der
p.OS-Programmierung werden wir das Kapitel
Intuition unter p.OS mit den Menüs und dem
EasyRequester abschließen. Zum besseren
Verständnis werden wir natürlich unser
Beispielprogramm aus den vorangegangenen Teilen
wieder erweitern. Es wird dann kein Problem mehr
für Sie sein, Programme unter p.OS zu entwickeln.
Da Ihnen p.OS die meisten Aufgaben beim Erstellen der Oberfläche abnimmt (Fontsensitiv, Menülayout, Gadgetpositionierung, Windowresizing, ...), können Sie sich ganz der Implementierung der gewünschten Aufgaben widmen. Unser Beispielprogramm wird Sie dabei zusätzlich unterstützen, indem Sie benötigte Programmteile (Gadgets/ Menüs/Messageloop/...) einfach per Cut-Copy-Paste übernehmen.
Beachten Sie dabei aber stets, daß für alle verwendeten Funktionen die entsprechenden Betriebsystem-Libraries und -Devices geöffnet sein müssen. Diese sind am Programmende dann wieder alle zu schließen. Beachten Sie auch, daß im Fehlerfall ein kontrollierter Programmabbruch stattfinden muß, bei dem alle reservieren Resourcen wieder freizugeben sind. Im Fehlerfall muß auch immer eine Mitteilung an den Benutzer erfolgen (z.B. per pOS_ PrintDosErr() oder pOS_ SPrintf()). Auch bei einem Workbenchstart unter p.OS dürfen Sie die pDOS-Ausgabefunktionen benutzen.
Im Bedarfsfall wird ein Console-Fenster zur Ausgabe
geöffnet. Bei Oberflächen-Programmen empfiehlt es
sich aber, den Fehler über Requester (z.B.
pOS_EasyRequest Args()) anzuzeigen. Die
Fehlertexte sollten dabei für jederman
verständlich und ausführlich sein. Statt »Lock
failed« verwenden Sie besser »Datei %s ist nicht
zu finden.«.
Dabei sollte auch der (vollständige)
Dateiname angegeben werden. Falls Sie zusätzliche
Informationen bei einer Fehlermeldung wünschen,
können Sie z.B. einen programmspezifischen
Fehlercode in Klammern mit ausgeben lassen.
Beachten Sie, daß im Fehlerfall unerfahrene
Benutzer meist nur den halben Fehlertext, ohne
weitere Angaben, wiedergeben können. Erfahrene
User werden bei ausführlichen Fehlertexten den
Fehler meist selbständig beheben.
Auf die ANSI-Funktionen exit()/atexit()/abort() muß im
Fehlerfall unbedingt verzichtet werden, da diese
intern auf Amiga-Funktionen zurückgreifen, was
unweigerlich zu einem Programmfehler führt. In
Zukunft wird Ihnen dafür die Funktion
pOS_ExitProcess() zur Verfügung stehen. Es ist
aber immer besser, wenn die Programme (und auch
die Funktionen) nur einen Ausstiegspunkt (eine
Return-Anweisung) enthalten und dadurch auf
harten Programmausstieg verzichten. Jedes
Programm (zumindest bei strukturierten
Programmiersprachen wie C oder C++) läßt sich so
entwickeln, daß diese Bedingung zutrifft. C++
(Version 3) bietet hierzu auch das
Exception-Handling an.
Damit ist es sehr einfach,
aus jeder tiefen Unterfunktion wieder zu einem
kontrollierbaren Punkt zurückzukehren. Nach
Möglichkeit sollte das Programm nach der
Fehlermeldung (evtl. auf Benutzerwunsch)
weiterlaufen.
![]() |
Programmprojekt unter p.OS: Unser selbstgestaltetes Fenster hat noch ein Menü bekommen |
- DOSFAIL_OK (0) : Programm wurde fehlerlos abgearbeitet - DOSFAIL_WARN (5) : Warnung, z.B. bei fehlerhaften Parametern - DOSFAIL_ABORT (8) : Benutzerabbruch durch CTRL-C - DOSFAIL_ERROR (10) : einfacher Fehler - DOSFAIL_FAIL (20) : großer Fehler, z.B. Speichermangel
Der zweite Punkt, der nur wenigen Anwendern auffallen
wird, ist, daß das Window während der Menüauswahl
nicht mehr gesperrt ist. Dadurch entfällt eine
mögliche Deadlock-Situation, wie sie beim Amiga
auftreten könnte.
Die Menüstruktur wird, ähnlich
den Gadgets, nur beschrieben. Die Menüanordnung
und Positionierung übernimmt das OS wieder
vollständig für Sie. Die Menüeinträge sind
wiederum Objekte, wodurch auch Grafiken und
Animationen als Einträge kein Problem sind. Die
Menüstruktur finden Sie in »p:pIntui/Menu.h«.
Interessant sind hierbei mt_Type für
Title/Item/Sub, mt_Lable für den Texteintrag und
mt_CommKey für einen Shortkey. Seltener benötigt
werden mt_Flags (z.B. zum Abhaken) und
mt_MutualExclude (für Ausschlußpunkte). Die
angesprochenen Grafiken und Animationen bekommen
Sie ins Menü über eine IntuiObj-Beschreibung in
mt_Tags. Das Ende der Menübeschreibung wird per
mt_Type MENUTAGTYP_End abgeschlossen.
|
Mit MENUITF_Toggle wird bei jeder
Auswahl der Hakenzustand gewechselt. Ob ein Punkt
aktiviert ist, können Sie beim Start über
MENUTIF_ IsChecked festlegen. Die Wählbarkeit
definiert man über MENUITF_Disabled (gesetzt =
nicht wählbar).
Einer Erklärung bedarf noch der
Eintrag für mt_MutualExclude. Die Funktionsweise
ist ähnlich den MX-Gadgets. Bei der Auswahl eines
Menüpunktes bestimmt die Maske in
mt_MutualExclude, welche anderen Menüpunkte
auszuschalten sind.
Es lassen sich nur die
Einträge der aktuellen Menüebene beeinflussen.
Dabei werden maximal die ersten 32 Einträge
beeinflußt (32 Bits im ULONG). Besser
verdeutlichen läßt sich das an einem Beispiel,
etwa die Style-Auswahl für eine Schrift. Hierbei
kann entweder Plain oder eine Kombination aus
Bold, Italic und Underline auftreten. Dabei ist
bei der Auswahl von Plain immer dieser Punkt
abzuhaken, alle anderen muß man deaktivieren. Die
anderen drei Punkte können einzeln ein- und
ausgeschaltet werden. Aus dieser Anforderung
ergibt sich folgende Menübeschreibung:
mt_Lable = "Plain" mt_Flags = MENUITF_Hook | MENUITF_IsChecked mt_MutualExclude = 0x0E /* %00001110 => B/I/U ausschalten */ mt_Lable = "Bold" mt_Flags = MENUITF_Hook | MENUITF_Toggle mt_MutualExclude = 0x01 /* %00000001 => Plain aus */ mt_Lable = "Italic" mt_Flags = MENUITF_Hook | MENUITF_Toggle mt_MutualExclude = 0x01 /* %00000001 => Plain aus */ mt_Lable = "Underline" mt_Flags = MENUITF_Hook | MENUITF_Toggle mt_MutualExclude = 0x01 /* %00000001 => Plain aus */
Der Eintrag Plain ist beim Start abgehakt und besitzt kein Toggle-Flag, um diesen Punkt bei mehrfacher Auswahl von Plain nicht zu deaktiveren, wodurch kein Punkt mehr aktiv wäre. Es gibt aber trotzdem eine Situation, die das OS nicht abfangen kann. Wählen Sie z.B. Bold, wird der Punkt abgehakt und Plain deaktiviert. Bei der zweiten Auswahl von Bold wird der Punkt wieder deaktivert und keiner der vier Punkte ist mehr gesetzt. Diesen Fall müssen Sie zwangsläufig selbst im Messageloop abfangen (vereinfachte Darstellung):
if( pOS_GetWindowMenuChecker(0)==FALSE && pOS_GetWindowMenuChecker(1)==FALSE && pOS_GetWindowMenuChecker(2)==FALSE && pOS_GetWindowMenuChecker(3)==FALSE) pOS_SetWindowMenuChecker(0,TRUE);
Einfacher wäre die Aufgabe, wenn ein Punkt immer alle anderen ausschließen würde: In diesem Fall geben Sie für jeden Punkt nur das Hook-Flag an. Dadurch wird bei doppelter Auswahl desselben Punktes kein Toggle durchgeführt und der Punkt bleibt weiterhin angewählt. Die vier MutualExclude-Einträge lauten dann:
0x0E /* %00001110 */ 0x0D /* %00001101 */ 0x0B /* %00001011 */ 0x07 /* %00000111 */
Die Maske
läßt sich, wie bereits erwähnt, auf die ersten 32
Menüeinträge ausdehnen. Beachten Sie, daß
Trennlinien auch einen Eintrag darstellen, der
nicht beeinflußbar ist und daher in der Maske
durch eine 0 angezeigt werden sollte.
Die MX-Gadgets arbeiten intern nach demselben Schema.
Allerdings ist hier die »nur einer
aktiv«-Schaltung vorgegeben. Ein anderes
Verhalten können Sie über ICLTAG_ MutualExclude
als Maske übergeben.
Zur Laufzeit kann der
Aktivierungszustand/Wählbarkeit über
pOS_MenuItem->mi_ Flags ermittelt werden. Zum
Ändern existieren die Funktionen
pOS_SetWindowMenuChecker() und
pOS_EnableWindowMenu(). Die dabei benötigte
pOS_MenuNum setzen Sie wie folgt zusammen:
Entweder tragen Sie den Wert als ULONG oder die
drei Einzelkomponenten als UBYTEs ein. Die Punkte
werden jeweils ab 0 gezählt und können
theoretisch bis maximal 254 laufen. Die Zahl 255
(= 0xFF) hat eine Sonderstellung und bedeutet,
daß dieser Eintrag nicht beachtet werden soll
(wenn beispielsweise kein Sub-Menü existiert).
Die Menünummern für das zweite Menü, dritter Eintrag (kein Sub) würde sich somit aus
pOS_MenuNum->men_U.men_Pck[MENNUPCK_Title] = 1 pOS_MenuNum->men_U.men_Pck[MENNUPCK_Item] = 2 pOS_MenuNum->men_U.men_Pck[MENNUPCK_Sub] = 0xFF
oder aus
pOS_MenuNum->men_U.men_Num = 0x0102FF00
zusammensetzen. Über diese Menünummern können Sie auch die Adresse des Menüeintrags per pOS_GetMenuItemFromNum() ermitteln. Über pOS_CreateMenuTagA() erzeugen Sie aus der Menübeschreibung die interne Menüstruktur, die noch über pOS_PreLayoutMenu() vorbereitet werden muß. Danach kann sie bei Bedarf per pOS_SetMenuStrip() in das zugehörige Fenster eingehängt werden. Verwenden Sie mehrere Fenster, sollte das Menü in jedem Fenster verfügbar sein. Dabei dürfen Sie den einen Menüzeiger in mehreren Fenstern verwenden. Am Programmende ist die interne Menüstruktur mit pOS_DeleteMenu() freizugeben.
Nach der Menüauswahl sendet
Intuition eine IDCMP_ MenuPick-Message. Dabei ist
9 im_Code-Feld die gewählte Menünummer vermerkt.
Die Menüverteilung kann wieder mit einer
verschachtelten Switch-Case-Konstellation
ausgewertet werden. Bedenken Sie, daß der erste
Eintrag immer 0 lautet. Alternativ können Sie
statt der Zahlen auch auf Defines oder
Enumerations zurückgreifen, um auch nach
Menüumbauten keine großen Änderungen im
Messagelloop durchführen zu müssen. Eine andere
Möglichkeit ist, im User-Zeiger des Menüs eine
Funktions-Adresse abzulegen, die man nur noch
aufrufen muß.
Das p.OS-StyleGuide schreibt vor,
daß Menüpunkt-Shortcuts, im Gegensatz zu Gadgets,
nicht lokalisiert werden. Die gängigen Punkte und
Shortcuts lauten somit:
Bei Auswahl eines
»Jahreszeiten«-Menüeintrages wird die Wahl, wie
bereits bei den Gadgets besprochen, über die
zentrale Funktion SetDatas() für alle Gadgets
aktiviert. Neu in dieser Funktion ist, daß die
aktuelle Auswahl über pOS_ SetWindowMenuChecker
auch im Menü gesetzt wird.
Bei der Auswahl von
»Quit« wird, wie nicht anders zu erwarten, das
Programm beendet. Dabei könnte z.B. noch eine
Abfrage eingebaut werden, ob das Programm
wirklich beendet oder ob evtl. veränderte Daten
noch gespeichert werden sollen.
Project-Menü | weitere Menüs | Edit-Menü | |||
New | Neu (N) | Help | Hilfe (?) | Cut | Ausschneiden (X) |
Open | Laden (O) | Copy | Kopieren (C) | ||
Save | Speichern (S) | Paste | Einfügen (V) | ||
Save As | Speichern unter (A) | Delete | Löschen | ||
Drucken (P) | Undo | Zurücknehmen (Z) | |||
Info | Information | Redo | Wiederherstellen | ||
Quit | Beenden (Q) |
Die normalen Requester haben aber einen Nachteil sie arbeiten synchron. Die Funktion »pOS_EasyRequestArgs()« kehrt erst dann zurück, wenn der Benutzer ein Gadget aus dem Requester anklickt, bzw. (optional) eine Nachricht entsprechend der gesetzten IDCMP-Flags aufgetreten ist. Während dieser Zeit kann Ihr Programm auch nicht auf Refresh-Anforderungen durch das OS reagieren. Hierbei zeigt sich aber der Vorteil, wenn das Fenster nur Gadgets enthält (z.B. auch für normale Textausgaben). Solche Gadgets können sich nämlich unabhängig vom Programm selbständig neu zeichnen und aufbauen.
Sollten Sie aber dennoch in die Situation kommen, daß während der Requesterausgabe das Programm weiterlaufen bzw. auf andere Nachrichten reagiert werden soll, existieren hierfür drei Funktionen. Mit »pOS_CreateRequestWin()« erzeugen Sie das Fenster identisch wie bei synchronen Requestern. Dabei erhalten Sie einen pOS_Window-Zeiger, den Sie zum Schließen des Requesters an »pOS_DeleteRequestWin()« übergeben müssen.
Einen normaler Messageloop könnte folgendermaßen aussehen:
ULONG sigs=pOS_WaitSignal( (1L < window->win_UserPort->mp_SigBit) | (1L < requester->win_UserPort->mp_SigBit) | DOSSIGF_CTRL_C);
Eine häufige Anwendung könnte aber auch sein, daß der EasyRequester nur einen Abbruch-Schalter enthält, mit der sich die aktuelle Berechnung durch das Programm vorzeitig beenden läßt. Dann müssen Sie regelmäßig mit »pOS_RequestWinHandler()« den Zustand des Requesters abfragen. Die Funktion erzeugt folgende Returnwerte:
2: es liegt keine
Nachricht vor (nur wenn Handler-Wait auf FALSE
gesetzt wurde)
1: ein gesetztes IDCMP-Ereignis
ist eingetreten
0: Schalter ganz rechts (meist
Abbruch) wurde angeklickt (bzw. einziger
vorhandener)
größer 0: Schalterposition
fortlaufend von links ab 1 gezählt
Beachten Sie,
daß das Schließen des Requesters nicht
automatisch geschieht, sondern mit
»pOS_DeleteRequestWin()« erledigt werden muß.
Wollen Sie den Requester mit einer
Fortschrittsanzeige versehen, finden Sie das
entsprechende Gadget »ObjATool_ProcessGadA.class«
in der »ObjATool.library«.
Damit haben wir alle Komponenten für ein vollständiges GUI-Programm zusammen: Windows, Gadgets, Menüs sowie deren Erzeugung und Auswertung. Der letzte Schritt sollte das Lokalisieren des Programms sein. Dabei wird das Amiga-kompatible Catalog-Konzept verwendet. Eine Catalog-Datei öffnen Sie mit pOS_OpenCatalog() und schließen Sie am Programmende mit pOS_CloseCatalog(). Zur Laufzeit können Sie die Texte per pOS_GetCatalogStr() abfragen. Als letzten Parameter erwartet die Funktion einen Default-String, der zurückgegeben wird, wenn der Catalog nicht geöffnet werden konnte, bzw. wenn der String im Catalog fehlt. Vor oder nach Nutzung der Funktionen müssen Sie natürlich die »pLocale.library« öffnen und schließen.
Die erste Aktion des Programms sollte das Öffnen der pLocale.library und des Catalogs sein. Dadurch kann auch die Template-Erklärung landesspezifisch erfolgen. Für das Template sowie die im Programm fest eingebaute Sprache, empfehlen wir Englisch. Vor allem im Public Domain-Bereich kann durch die Catalog-Dateien eine große (sprachenunabhängige) Verbreitung erfolgen. Es findet sich immer jemand, der für eine fehlende Sprache die Cataloge ergänzt und somit das Programm noch mehr Benutzern zugänglich macht. Dabei sollte man aber auch immer die Anleitungsdatei mit übersetzen.
Auf die Lokalisierung eines Programms werden wir aber erst in lose folgenden, künftigen Kursteilen eingehen. Die Entwicklerunterlagen enthalten neben den Autodocs zu allen Libraries/Devices/Objekten auch jede Menge Beispielprogramme, die auf einzelne Funktionen eingehen. Bei weiteren Fragen können Sie sich direkt per E-Mail (»develop@ prodad.de«) an proDAD wenden. Mittlerweile existiert für p.OS auch eine Mailing-List, in der Sie als registrierter Entwickler Fragen und Erfahrungen weitergeben können.
© Copyright by MagnaMedia Verlag AG, Haar bei München
Veröffentlichung und Vervielfältigung nur mit schriftlicher Genehmigung des Verlags